package com.znv.sentinelrepository;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity;
import com.alibaba.csp.sentinel.dashboard.repository.metric.MetricsRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

import javax.annotation.PostConstruct;

/**
 * @author pengYongQiang
 * @version 1.0
 * @date 2020/7/8 15:20
 * @see AutoExcludeAutoConfigEnvironmentPostProcessor
 * 根据 znv.sentinel.metric.repository.metric-repository-type 参数
 * 自动排除 spring-boot-autoconfigure包中的自动配置类
 * @see MetricRepositoryType
 * 持久化方式的枚举类型
 * @see MetricRepositoryProperties
 * 配置参数映射
 *
 *
 * 1.如果znv.sentinel.metric.repository.metric-repository-type=mysql
 * 那么将组件 inMemoryMetricsRepository 从容器的注册表移除，否则
 * MetricsRepository类型的组件无法注入
 * 2.如果没有配 znv.sentinel.metric.repository.metric-repository-type
 * 那么将 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
 * 自动配置移除
 */

@Configuration
@EnableConfigurationProperties(MetricRepositoryProperties.class)
@ComponentScan("com.znv.sentinelrepository")
@EnableJpaRepositories("com.znv.sentinelrepository")
@EntityScan("com.znv.sentinelrepository")
public class MetricRepositoryAutoConfiguration {

    private final static Logger LOG = LoggerFactory.getLogger(MetricRepositoryAutoConfiguration.class);

    //(官方实现)默认实现持久化组件名称
    private final static String DEFAULT_REPOSITORY_COMPONENT_NAME = "inMemoryMetricsRepository";
    //MySql实现持久化组件名称
    private final static String MYSQL_REPOSITORY_COMPONENT_NAME = "InMySqlMetricsRepository";
    //MongoDB实现持久化组件名称
   // private final static String MONGO_REPOSITORY_COMPONENT_NAME = "inMongoDBMetricsRepository";

    @PostConstruct
    public void init() {
        LOG.info("\nactive MetricRepositoryAutoConfiguration");
    }

    /**
     * MySql自动配置类
     * 1.注册 MySql 持久化组件
     * 2.移除 Memory 持久化组件
     */
    @Configuration
    @ConditionalOnProperty(prefix = "znv.sentinel.metric.repository"
            , name = "metric-repository-type", havingValue = "mysql")
    protected static class MysqlMetricRepositoryAutoConfiguration {
        @PostConstruct
        public void init() {
            LOG.info("\nsentinel metric Repository:{} ,Remove Component:{}", "mysql", DEFAULT_REPOSITORY_COMPONENT_NAME);
        }

        @Bean(name = MYSQL_REPOSITORY_COMPONENT_NAME)
        public MetricsRepository<MetricEntity> InMySqlMetricsRepository() {
            return new InMySqlMetricsRepository();
        }

        @Bean
        public BeanDefinitionRegistryPostProcessor removeInMemoryMetricsRepositoryFormBDRegistry() {
            return new RemoveMemoryMetricsRepositoryFormBDRegistry();
        }
    }

    /**
     * 操作注册表，移除(官方)默认实现持久化组件，inMemoryMetricsRepository
     */
    public static class RemoveMemoryMetricsRepositoryFormBDRegistry implements BeanDefinitionRegistryPostProcessor {

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
            beanDefinitionRegistry.removeBeanDefinition(DEFAULT_REPOSITORY_COMPONENT_NAME);
            LOG.info("\nremove the {} BeanDefinition from the beanDefinitionRegistry", DEFAULT_REPOSITORY_COMPONENT_NAME);
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

        }
    }

}
